import java.util.*;

class Coordinate {
	
	public double x;
	public double y;
	
	public Coordinate(double x, double y, double side) {
		this.x = x*side;
		this.y = y*side;
	}
	
}

public class Model {
	
	private double x;
	private double y;
	private double theta;
	private double side;
	private HashMap<String,Coordinate> field;
	volatile Vector<String> top, right, bottom, left;
	
	public Model(String side) {
		if (side.equals("left")) {
			this.side = 1.0;
		} else {
			this.side = -1.0;
		}
		this.field = new HashMap<String,Coordinate>();
		//flags outside the players field
		this.field.put("f t 0", new Coordinate(0.0,-39.0,this.side)); // flag t 0
		this.field.put("f t r 10", new Coordinate(10.0,-39.0,this.side)); // flag t r 10
		this.field.put("f t r 20", new Coordinate(20.0,-39.0,this.side)); // flag t r 20
		this.field.put("f t r 30", new Coordinate(30.0,-39.0,this.side)); // flag t r 30
		this.field.put("f t r 40", new Coordinate(40.0,-39.0,this.side)); // flag t r 40
		this.field.put("f t r 50", new Coordinate(50.0,-39.0,this.side)); // flag t r 50
		this.field.put("f r t 30", new Coordinate(57.5,-30.0,this.side)); // flag r t 30
		this.field.put("f r t 20", new Coordinate(57.5,-20.0,this.side)); // flag r t 20
		this.field.put("f r t 10", new Coordinate(57.5,-10.0,this.side)); // flag r t 10
		this.field.put("f r 0", new Coordinate(57.5,0.0,this.side)); // flag r 0
		this.field.put("f r b 10", new Coordinate(57.5,10.0,this.side)); // flag r b 10
		this.field.put("f r b 20", new Coordinate(57.5,20.0,this.side)); // flag r b 20
		this.field.put("f r b 30", new Coordinate(57.5,30.0,this.side)); // flag r b 30
		this.field.put("f b r 50", new Coordinate(50.0,39.0,this.side)); // flag r b 50
		this.field.put("f b r 40", new Coordinate(40.0,39.0,this.side)); // flag r b 40
		this.field.put("f b r 30", new Coordinate(30.0,39.0,this.side)); // flag r b 30
		this.field.put("f b r 20", new Coordinate(20.0,39.0,this.side)); // flag r b 20
		this.field.put("f b r 10", new Coordinate(10.0,39.0,this.side)); // flag r b 10
		this.field.put("f b 0", new Coordinate(0.0,39.0,this.side)); // flag b 0
		this.field.put("f b l 10", new Coordinate(-10.0,39.0,this.side)); // flag b l 10
		this.field.put("f b l 20", new Coordinate(-20.0,39.0,this.side)); // flag b l 20
		this.field.put("f b l 30", new Coordinate(-30.0,39.0,this.side)); // flag b l 30
		this.field.put("f b l 40", new Coordinate(-40.0,39.0,this.side)); // flag b l 40
		this.field.put("f b l 50", new Coordinate(-50.0,39.0,this.side)); // flag b l 50
		this.field.put("f l b 30", new Coordinate(-57.5,30.0,this.side)); // flag l b 30
		this.field.put("f l b 20", new Coordinate(-57.5,20.0,this.side)); // flag l b 20
		this.field.put("f l b 10", new Coordinate(-57.5,10.0,this.side)); // flag l b 10
		this.field.put("f l 0", new Coordinate(-57.5,0.0,this.side)); // flag l 0
		this.field.put("f l t 10", new Coordinate(-57.5,-10.0,this.side)); // flag l t 10
		this.field.put("f l t 20", new Coordinate(-57.5,-20.0,this.side)); // flag l t 20
		this.field.put("f l t 30", new Coordinate(-57.5,-30.0,this.side)); // flag l t 30
		this.field.put("f t l 50", new Coordinate(-50.0,-39.0,this.side)); // flag t l 50
		this.field.put("f t l 40", new Coordinate(-40.0,-39.0,this.side)); // flag t l 40
		this.field.put("f t l 30", new Coordinate(-30.0,-39.0,this.side)); // flag t l 30
		this.field.put("f t l 20", new Coordinate(-20.0,-39.0,this.side)); // flag t l 20
		this.field.put("f t l 10", new Coordinate(-10.0,-39.0,this.side)); // flag t l 10
		//Flags in the boundary of the player field
		this.field.put("f c", new Coordinate(0.0,0.0,this.side)); // flag c
		this.field.put("f c t", new Coordinate(0.0,-34.0,this.side)); //flag c t
		this.field.put("f r t", new Coordinate(52.5,-34.0,this.side)); //flag r t
		this.field.put("f r b", new Coordinate(52.5,34.0,this.side)); //flag r b
		this.field.put("f c b", new Coordinate(0.0,34.0,this.side)); //flag c b
		this.field.put("f l b", new Coordinate(-52.5,34.0,this.side)); //flag l b
		this.field.put("f l t", new Coordinate(-52.5,-34.0,this.side)); //flag l t
		//Flags for the left goal
		this.field.put("g l", new Coordinate(-52.5,0.0,this.side)); //goal l
		this.field.put("f g l t", new Coordinate(-52.5,-7.0,this.side)); // flag g l t
		this.field.put("f p l t", new Coordinate(-36.0,-20.0,this.side)); //flag p l t
		this.field.put("f p l c", new Coordinate(-36.0,0.0,this.side)); //flag p l c
		this.field.put("f p l b", new Coordinate(-36.0,20.0,this.side)); //flag p l b
		this.field.put("f g l b", new Coordinate(-52.5,7.0,this.side)); //flag g l b
		//Flags for the right goal
		this.field.put("g r", new Coordinate(52.5,0.0,this.side)); //goal l
		this.field.put("f g r t", new Coordinate(52.5,-7.0,this.side)); // flag g l t
		this.field.put("f p r t", new Coordinate(36.0,-20.0,this.side)); //flag p l t
		this.field.put("f p r c", new Coordinate(36.0,0.0,this.side)); //flag p l c
		this.field.put("f p r b", new Coordinate(36.0,20.0,this.side)); //flag p l b
		this.field.put("f g r b", new Coordinate(52.5,7.0,this.side)); //flag g l b
		this.x = 0.0;
		this.y = 0.0;
		this.top = new Vector<String>();
		this.right = new Vector<String>();
		this.bottom = new Vector<String>();
		this.left = new Vector<String>();
		this.theta = 0.0;
	}
	
	public double getX() {
		return this.x;
	}
	
	public double getY() {
		return this.y;
	}
	public double getTheta() {
		return this.theta;
	}
	
	public void updateXY(double x, double y) {
		this.x = x;
		this.y = y;
	}

	public void updateWorld(HashMap<String,String> map) {
		Set<String> s = map.keySet();
		Iterator<String> i = s.iterator();
		Vector<Double> xs = new Vector<Double>();
		Vector<Double> ys = new Vector<Double>();
		Vector<Double> angles = new Vector<Double>();
		Vector<String> flags = new Vector<String>();
		while(i.hasNext()) {
			flags.add(i.next());
		}
		for (int j = 0; j < flags.size() - 1; j++) {
			for (int k = j + 1; k < flags.size() - 1; k++) {
				Coordinate c0 = field.get(flags.elementAt(j));
				Coordinate c1 = field.get(flags.elementAt(k));
				String parameter = map.get(flags.elementAt(j));
				String[] parameters = parameter.split(" ");
				double d0 = Double.parseDouble(parameters[0]);
				double alpha0 = Double.parseDouble(parameters[1]); //direction to point0
				parameter = map.get(flags.elementAt(k));
				parameters = parameter.split(" ");
				double d1 = Double.parseDouble(parameters[0]);
				double alpha1 = Double.parseDouble(parameters[1]); //direction to point1
				double k0 = Math.pow(c0.x, 2.0) + Math.pow(c0.y, 2.0) - Math.pow(d0, 2.0);
				double k1 = Math.pow(c1.x, 2.0) + Math.pow(c1.y, 2.0) - Math.pow(d1, 2.0);
				double x0, y0;
				double x1, y1;
				double C, B;
				boolean discriminante = true;
				if (c0.x == c1.x) {
					double y = (k1 - k0) / (2.0 * (c1.y - c0.y));
					y0 = y;
					y1 = y;
					B = -2.0 * c0.x;
					C = Math.pow(y, 2.0) - 2.0 * y * c0.y + k0;
					x0 = (-B + Math.sqrt(Math.pow(B, 2.0) - 4 * C)) / 2.0;
					x1 = (-B - Math.sqrt(Math.pow(B, 2.0) - 4 * C)) / 2.0;
				} else {
					if (c0.y == c1.y) {
						double x = (k1 - k0) / (2.0 * (c1.x - c0.x));
						x0 = x;
						x1 = x;
						B = -2.0 * c0.y;
						C = Math.pow(x, 2.0) - 2.0 * x * c0.x + k0;
						y0 = (-B + Math.sqrt(Math.pow(B, 2.0) - 4 * C)) / 2.0;
						y1 = (-B - Math.sqrt(Math.pow(B, 2.0) - 4 * C)) / 2.0;
					} else {
						double M = (k1 - k0) / (2.0 * (c1.x - c0.x));
						double N = (c0.y - c1.y) / (c1.x - c0.x);
						B = (2.0 * (M * N - N * c0.x - c0.y)) / (Math.pow(N, 2.0) + 1.0);
						C = (k0 + Math.pow(M, 2.0) - 2.0 * M * c0.x) / (Math.pow(N, 2.0) + 1.0);
						if (Math.pow(B, 2.0) - 4 * C < 0) {
							discriminante = false;
						}
						if (discriminante) {
							y0 = (-B + Math.sqrt(Math.pow(B, 2.0) - 4 * C)) / 2.0;
							x0 = M + y0 * N;
							y1 = (-B - Math.sqrt(Math.pow(B, 2.0) - 4 * C)) / 2.0;
							x1 = M + y1 * N;
						} else {
							x0 = 0.0;
							y0 = 0.0;
							x1 = 0.0;
							y1 = 0.0;
						}
					}
				}
				if (discriminante) {
					if (checkSolution(c0,c1,x0,y0,x1,y1)) {
						xs.add(x0);
						ys.add(y0);
						double gamma0 = Math.toDegrees(Math.atan2(c0.y - y0,c0.x - x0));
						if (gamma0 >= 0) {
							gamma0 = gamma0 - alpha0;
						} else {
							gamma0 = (360.0 + gamma0) - alpha0;
						}
						if (gamma0 >= 360.0) {
							gamma0 = gamma0 - 360.0;
						}
						if (gamma0 < 0) {
							gamma0 = gamma0 + 360.0;
						}
						double gamma1 = Math.toDegrees(Math.atan2(c1.y - y0,c1.x - x0));
						if (gamma1 >= 0) {
							gamma1 = gamma1 - alpha1;
						} else {
							gamma1 = (360.0 + gamma1) - alpha1;
						}
						if (gamma1 > 360.0) {
							gamma1 = gamma1 - 360.0;
						}
						if (gamma1 < 0) {
							gamma1 = gamma1 + 360.0;
						}
						double gamma = this.midPoint(gamma0, gamma1);
						angles.add(gamma);
					} else {
						xs.add(x1);
						ys.add(y1);
						double gamma0 = Math.toDegrees(Math.atan2(c0.y - y1,c0.x - x1));
						if (gamma0 >= 0) {
							gamma0 = gamma0 - alpha0;
						} else {
							gamma0 = (360.0 + gamma0) - alpha0;
						}
						if (gamma0 >= 360.0) {
							gamma0 = gamma0 - 360.0;
						}
						if (gamma0 < 0) {
							gamma0 = gamma0 + 360.0;
						}
						double gamma1 = Math.toDegrees(Math.atan2(c1.y - y1,c1.x - x1));
						if (gamma1 >= 0) {
							gamma1 = gamma1 - alpha1;
						} else {
							gamma1 = (360.0 + gamma1) - alpha1;
						}
						if (gamma1 > 360.0) {
							gamma1 = gamma1 - 360.0;
						}
						if (gamma1 < 0) {
							gamma1 = gamma1 + 360.0;
						}
						double gamma = this.midPoint(gamma0, gamma1);
						angles.add(gamma);
					}
				}
			}
		}
		this.x = this.average(xs);
		this.y = this.average(ys);
		double gamma = angles.elementAt(0);
		for (int j = 1; j < angles.size(); j++) {
			gamma = this.midPoint(gamma, angles.elementAt(j));
		}
		this.theta = gamma;
		System.out.println("x: " + Double.toString(this.x) + ", y: " + Double.toString(this.y) + ", theta: " + Double.toString(this.theta));
	}
	
	public boolean checkSolution(Coordinate c0, Coordinate c1, double x0, double y0, double x1, double y1) {
		boolean flag = false;
		double d0 = Math.sqrt(Math.pow(x0 - this.x, 2.0) + Math.pow(y0 - this.y, 2.0));
		double d1 = Math.sqrt(Math.pow(x1 - this.x, 2.0) + Math.pow(y1 - this.y, 2.0));
		if (d0 < d1) {
			flag = true;
		}
		return flag;
	}
	
	public double average(Vector<Double> data) {
		double suma = 0.0;
		double promedio = 0.0;
		for (int i = 0; i < data.size(); i++) {
			suma = suma + data.elementAt(i);
		}
		if (data.size() > 0) {
			promedio = suma / (double)data.size();
		}
		return promedio;
	}
	
	public double midPoint(double alpha0, double alpha1) {
		double midPoint;
		double midPoint0 = (alpha0 + alpha1) / 2.0;
		double midPoint1 = midPoint0 + 180.0;
		if (midPoint1 > 360.0) {
			midPoint1 = midPoint1 - 360.0;
		}
		if (this.minArc(alpha0, midPoint0) < this.minArc(alpha0, midPoint1)) {
			midPoint = midPoint0;
		} else {
			midPoint = midPoint1;
		}
		return midPoint;
	}
	
	public double minArc(double angle0, double angle1) { //angle0: source, angle1: objective
		double arc = Math.abs(angle1 - angle0);
		if (arc > 180.0) {
			arc = 360.0 - arc ;
		}
		return arc;
	}
	
	/*Test*/
	
	/*public static void main(String[] args) {
		Model m = new Model("left");
		m.updateXY(-20.0, 10.0);
		String see2 = "(see 0 ((f c) 22.4 -27 0 0) ((f r t) 84.8 -31) ((f r b) 76.7 18) ((f g r b) 72.2 -2) ((g r) 73 -8) ((f g r t) 74.4 -13) ((f p r b) 56.8 10) ((f p r c) 56.8 -10) ((f p r t) 63.4 -28) ((f t r 30) 70.1 -44) ((f t r 40) 77.5 -39) ((f t r 50) 85.6 -35) ((f b r 10) 41.7 44) ((f b r 20) 49.4 36) ((f b r 30) 58 30) ((f b r 40) 66.7 26) ((f b r 50) 75.9 23) ((f r 0) 78.3 -7) ((f r t 10) 79.8 -14) ((f r t 20) 83.1 -21) ((f r t 30) 87.4 -27) ((f r b 10) 77.5 0) ((f r b 20) 78.3 7) ((f r b 30) 79.8 14) ((b) 22.2 -27 0 0) ((l r) 72.2 90))";
		//String see2 = "(see 222 ((f r t) 47 -31) ((f g r b) 43.8 23) ((g r) 41.7 14) ((f g r t) 40.4 4) ((f p r c) 25.8 22 -0 0) ((f p r t) 26 -23 -0 -0) ((f t r 50) 47.9 -38) ((f r 0) 46.5 12) ((f r t 10) 45.6 0) ((f r t 20) 46.5 -13) ((f r t 30) 49.9 -24) ((f r b 10) 49.4 24) ((f r b 20) 54.6 33) ((f r b 30) 60.3 41) ((l r) 40.4 90))";
		//String see3 = "(see 6000 ((f c t) 21.1 8) ((f r t) 73.7 2) ((f r b) 102.5 44) ((f g r b) 85.6 31) ((g r) 82.3 27) ((f g r t) 79 22) ((f p r c) 68 33) ((f p r t) 59.1 16) ((f t 0) 21.1 -5) ((f t r 10) 31.2 -4) ((f t r 20) 40.9 -3) ((f t r 30) 50.9 -2) ((f t r 40) 60.9 -2) ((f t r 50) 70.8 -2) ((f t l 10) 11.1 -10 0 0) ((F) 2.2 -63) ((f r 0) 86.5 25) ((f r t 10) 83.1 19) ((f r t 20) 80.6 12) ((f r t 30) 79 5) ((f r b 10) 91.8 31) ((f r b 20) 96.5 36) ((f r b 30) 103.5 40))";
		Parser p = new Parser(see2);
		HashMap<String,String> objects = p.getMap();
		HashMap<String,String> flags = new HashMap<String,String>();
		Set<String> s = objects.keySet();
		Iterator<String> i = s.iterator();
		while (i.hasNext()) {
			String item = i.next();
			String[] items = item.split(" ");
			if (items[0].equals("f")) {
				if (items.length > 2) {
					//System.out.println("update: " + item + " => " + objects.get(item));
					flags.put(item, objects.get(item));
				}
				//this.world.updateWorld(this.flags);
			}
		}
		m.updateWorldDemo(flags);
		System.out.println("X:      " + Double.toString(m.getX()));
		System.out.println("Y:      " + Double.toString(m.getY()));
		System.out.println("Angle : " + Double.toString(m.getTheta()));
	}*/
	
}
